package top.mypath.model.dto;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.analysis.ExcelReadExecutor;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.fastjson2.JSON;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import org.apache.commons.collections4.CollectionUtils;
import top.mypath.model.MaxOrderCombine;
import top.mypath.model.Order;
import top.mypath.model.ProcessingEnum;
import top.mypath.page.ProgressBarDialog;
import top.mypath.service.NoModelDataListener;
import top.mypath.service.SplitService;

import java.io.File;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

/**
 * @author LiYalin
 */

public class CacheData {

    /**
     * 每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;

    private static ExcelReaderBuilder excelReaderBuilder;

    private static File excelFile;

    private static String filePath;

    private static List<ReadSheet> readSheets;

    private static List<SheetSelectItem> sheetSelectItems;

    private static SheetSelectItem selectSheetItem;

    private static Integer headRow;

    private static Map<Integer, String> headMap;

    private static List<HeadSelectItem> headSelectItems;

    private static HeadSelectItem headSelectItem;

    private static BigDecimal limitMoney;

    private static String taxRate;

    private static List<Map<Integer, String>> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    private static List<CheckBoxItem> showCheckBoxItems;

    private static Map<String,CheckBoxItem> showCheckBoxItemMap;

    private static List<CheckBoxItem> selectedCheckBoxItems;

    private static List<Order> tempOrders;

    private static Map<Integer,Map<Integer, String>> rawData;

    private static List<List<Object>> writeData;

    private static String splitManner;

    private static ProgressBarDialog dialog;

    private static File outputFile;

    private static String outputFilePath;

    private static String outputFileName;

    public static void setDialog(ProgressBarDialog dialog) {
        CacheData.dialog = dialog;
    }

    public static File getExcelFile() {
        return excelFile;
    }

    public static void setExcelFile(File excelFile) {
        CacheData.excelFile = excelFile;
    }


    public static String getFilePath() {
        return filePath;
    }

    public static void setFilePath(String filePath) {
        CacheData.filePath = filePath;
    }

    public static List<ReadSheet> getReadSheets() {
        return readSheets;
    }

    public static void setReadSheets(List<ReadSheet> readSheets) {
        CacheData.readSheets = readSheets;
    }

    public static SheetSelectItem getSelectSheetItem() {
        return selectSheetItem;
    }

    public static void setSelectSheetItem(SheetSelectItem selectSheetItem) {
        CacheData.selectSheetItem = selectSheetItem;
    }

    public static Integer getHeadRow() {
        return headRow;
    }

    public static void setHeadRow(Integer headRow) {
        headRow = headRow > 0 ? headRow : 1;
        CacheData.headRow = headRow;
        excelReaderBuilder.headRowNumber(headRow);
    }

    public static List<SheetSelectItem> getSheetSelectItems() {
        return sheetSelectItems;
    }

    public static void setSheetSelectItems(List<SheetSelectItem> sheetSelectItems) {
        CacheData.sheetSelectItems = sheetSelectItems;
    }

    public static List<Map<Integer, String>> getCachedDataList() {
        return cachedDataList;
    }

    public static void setCachedDataList(List<Map<Integer, String>> cachedDataList) {
        CacheData.cachedDataList = cachedDataList;
    }

    public static Map<Integer, String> getHeadMap() {
        return headMap;
    }

    public static void setHeadMap(Map<Integer, String> headMap) {
        CacheData.headMap = headMap;
        setHeadSelectItems(headMap);
    }

    public static List<HeadSelectItem> getHeadSelectItems() {
        return headSelectItems;
    }

    public static void setHeadSelectItems(List<HeadSelectItem> headSelectItems) {
        CacheData.headSelectItems = headSelectItems;
    }

    public static void setHeadSelectItems(Map<Integer, String> headMap) {
        if (headMap != null && !headMap.isEmpty()) {
            List<HeadSelectItem> list = new ArrayList<>();
            for (Map.Entry<Integer, String> entry : headMap.entrySet()) {
               list.add(new HeadSelectItem(entry.getKey(),entry.getValue()));
            }
            CacheData.headSelectItems = list;
        }
        CacheData.headSelectItems = headSelectItems;
    }

    public static HeadSelectItem getHeadSelectItem() {
        return headSelectItem;
    }

    public static void setHeadSelectItem(HeadSelectItem headSelectItem) {
        CacheData.headSelectItem = headSelectItem;
    }

    public static List<CheckBoxItem> getShowCheckBoxItems() {
        return showCheckBoxItems;
    }

    public static void setShowCheckBoxItems(List<CheckBoxItem> showCheckBoxItems) {
        CacheData.showCheckBoxItems = showCheckBoxItems;
    }

    public static List<CheckBoxItem> getSelectedCheckBoxItems() {
        if(null==selectedCheckBoxItems){
            selectedCheckBoxItems=new ArrayList<>();
        }
        return selectedCheckBoxItems;
    }

    public static void setSelectedCheckBoxItems(List<CheckBoxItem> selectedCheckBoxItems) {
        CacheData.selectedCheckBoxItems = selectedCheckBoxItems;
    }

    public static BigDecimal getLimitMoney() {
        return limitMoney;
    }

    public static void setLimitMoney(BigDecimal limitMoney) {
        CacheData.limitMoney = limitMoney;
    }

    public static String getTaxRate() {
        return taxRate;
    }

    public static void setTaxRate(String taxRate) {
        CacheData.taxRate = taxRate;
    }

    public static String getSplitManner() {
        return splitManner;
    }

    public static void setSplitManner(String splitManner) {
        CacheData.splitManner = splitManner;
    }

    public static Map<String, CheckBoxItem> getShowCheckBoxItemMap() {
        if(null==showCheckBoxItemMap){
            showCheckBoxItemMap=new HashMap<>();
        }
        return showCheckBoxItemMap;
    }

    public static void setShowCheckBoxItemMap(Map<String, CheckBoxItem> showCheckBoxItemMap) {
        CacheData.showCheckBoxItemMap = showCheckBoxItemMap;
    }

    public static File getOutputFile() {
        return outputFile;
    }

    public static void setOutputFile(File outputFile) {
        CacheData.outputFile = outputFile;
    }

    public static String getOutputFilePath() {
        return outputFilePath;
    }

    public static void setOutputFilePath(String outputFilePath) {
        CacheData.outputFilePath = outputFilePath;
    }

    public static String getOutputFileName() {
        return outputFileName;
    }

    public static void setOutputFileName(String outputFileName) {
        CacheData.outputFileName = outputFileName;
    }

    public static void readFileSheet() throws NoSuchFieldException, IllegalAccessException {
        excelReaderBuilder = EasyExcel.read(CacheData.getExcelFile());
        excelReaderBuilder.registerReadListener(new NoModelDataListener());
        ExcelReaderSheetBuilder sheet = excelReaderBuilder.sheet();
        Field excelReaderField = ExcelReaderSheetBuilder.class.getDeclaredField("excelReader");
        excelReaderField.setAccessible(true);
        ExcelReader excelReader = (ExcelReader) excelReaderField.get(sheet);
        ExcelReadExecutor excelReadExecutor = excelReader.excelExecutor();
        List<ReadSheet> readSheets = excelReadExecutor.sheetList();
        CacheData.setReadSheets(readSheets);
        List<SheetSelectItem> selectItems = readSheets.stream().map(SheetSelectItem::new).collect(Collectors.toList());
        CacheData.setSheetSelectItems(selectItems);
    }

    public static void readSheet() {
        cachedDataList.clear();
        ExcelReaderSheetBuilder sheet = excelReaderBuilder.sheet();
        ExcelReader excelReader = excelReaderBuilder.build();
        ReadSheet readSheet = sheet.sheetNo(selectSheetItem.getSheetNo()).build();
        List<List<String>> head = readSheet.getHead();
        System.out.println(JSON.toJSONString(head));
        excelReader.read(readSheet);
    }

    public static void cleaningData(){
        if(CollectionUtils.isNotEmpty(headSelectItems)&&CollectionUtils.isNotEmpty(cachedDataList)) {
            List<Integer> headNos = headSelectItems.stream().map(HeadSelectItem::getCellNo).collect(Collectors.toList());
            cachedDataList = cachedDataList.stream().filter(map -> {
                if (map.size() != headNos.size()) {
                    return Boolean.FALSE;
                } else {
                    for (Integer headNo : headNos) {
                        if (ObjectUtil.isEmpty(map.get(headNo))) {
                            return Boolean.FALSE;
                        }
                    }
                    return Boolean.TRUE;
                }
            }).collect(Collectors.toList());
        }
    }

    public static void createRawCheckBoxItem(){
        if(CollectionUtils.isNotEmpty(headSelectItems)){
            List<CheckBoxItem> rawCheckBoxItems = headSelectItems.stream().map(hsi -> {
                CheckBoxItem checkBoxItem = new CheckBoxItem();
                checkBoxItem.setSourceCellNo(hsi.getCellNo());
                checkBoxItem.setCellName(hsi.getCellName());
                checkBoxItem.setProcessingType(ProcessingEnum.RAW);
                return checkBoxItem;
            }).collect(Collectors.toList());
            if(null==showCheckBoxItems){
                showCheckBoxItems = new ArrayList<>();
            }
            showCheckBoxItems.addAll(rawCheckBoxItems);
            if(StringUtils.isBlank(CacheData.taxRate)){return;}
            CheckBoxItem taxRate = new CheckBoxItem("税率", ProcessingEnum.PADDING, CacheData.taxRate);
            if(Objects.isNull(CacheData.getHeadSelectItem())){return;}
            String formula="("+CacheData.getHeadSelectItem().getCellName()+"/(税率+1))*税率";
            CheckBoxItem tax = new CheckBoxItem("税额", ProcessingEnum.FORMULA, formula);
            tax.setFormula(formula);
            showCheckBoxItems.add(taxRate);
            showCheckBoxItems.add(tax);
        }
    }

    public static void dataConversion(){
        if(null==tempOrders){
            tempOrders=new ArrayList<>();
        }
        if(null==rawData){
            rawData = new HashMap<>();
        }
        if(CollectionUtils.isNotEmpty(cachedDataList)){
            for (int i = 0; i < cachedDataList.size(); i++) {
                Map<Integer, String> map = cachedDataList.get(i);
                rawData.put(i,map);
                String amountString = map.get(headSelectItem.getCellNo());
                Order order = new Order();
                order.setBatchNumber(String.valueOf(i));
                order.setTotal(BigDecimal.valueOf(Double.valueOf(amountString)));
                tempOrders.add(order);
            }
            cachedDataList.clear();
        }
    }


    public static List<MaxOrderCombine> splitInvoice(){
        SplitService.setQuota(limitMoney);
        List<MaxOrderCombine> maxOrderCombines;
        if(splitManner.equals("cupidity")){
            maxOrderCombines = SplitService.splitData(tempOrders);
        }else{
            maxOrderCombines = SplitService.orderSplitData(tempOrders);
        }
        return maxOrderCombines;
    }


    public static List<List<String>> createSheetHead(){
        List<List<String>> headers = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(selectedCheckBoxItems)){
            headers = selectedCheckBoxItems.stream().map(cbi->{
                List<String> cellHead = new ArrayList<>();
                cellHead.add(cbi.getCellName());
                return cellHead;
            }).collect(Collectors.toList());
            List<String> cellHead = new ArrayList<>();
            cellHead.add("发票");
            headers.add(cellHead);
        }
        return headers;
    }

    public static void createWriteData(ProgressBarDialog dialog){
        List<List<Object>> table = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(selectedCheckBoxItems)){
            ExpressRunner runner = new ExpressRunner(true, false);
            Thread thread = new Thread(() -> {
                for (int i = 0; i < 29; i++) {
                    try {
                        dialog.setCompletionRate(i);
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            thread.start();
            List<MaxOrderCombine> maxOrderCombines = splitInvoice();
            thread.stop();
            dialog.setCompletionRate(30);
            for (MaxOrderCombine maxOrderCombine : maxOrderCombines) {
                List<String> batchNumbers = maxOrderCombine.getBatchNumbers();
                for (String batchNumber : batchNumbers) {
                    List<Object> row = new ArrayList<>();
                    Map<Integer, String> rawRow = rawData.get(Integer.valueOf(batchNumber));
                    for (CheckBoxItem selectedCheckBoxItem : selectedCheckBoxItems) {
                        switch (selectedCheckBoxItem.getProcessingType()){
                            case PADDING:
                                row.add(selectedCheckBoxItem.getValue());
                                break;
                            case RAW:
                                row.add(rawRow.get(selectedCheckBoxItem.getSourceCellNo()));
                                break;
                            case FORMULA:
                                if(headSelectItem!=null && StringUtils.isNotBlank(taxRate)) {
                                    try {
                                        IExpressContext<String, Object> context = new DefaultContext<String, Object>();
                                        context.put(headSelectItem.getCellName(), Double.parseDouble(rawRow.get(headSelectItem.getCellNo())));
                                        context.put("税率", Double.parseDouble(taxRate));
                                        BigDecimal tax = (BigDecimal) runner.execute(selectedCheckBoxItem.getFormula(), context, null, true, false);
                                        tax.setScale(2, BigDecimal.ROUND_HALF_UP);
                                        row.add(tax);
                                    }catch (Exception e){
                                        e.printStackTrace();
                                        row.add("计算错误");
                                    }
                                }
                                break;
                            default:break;
                        }
                    }
                    row.add(maxOrderCombine.getInvoiceSerialNumber());
                    table.add(row);
                    int i = (int) ((new Float(table.size()) / tempOrders.size())*30);
                    Integer completionRate=30+i;
                    dialog.setCompletionRate(completionRate);
                }
            }
            writeData=table;
        }
    }

    public static void outputFile(ProgressBarDialog dialog){
        setDialog(dialog);
        if(StringUtils.isNotBlank(outputFilePath)&&StringUtils.isNotBlank(outputFileName)&&CollectionUtils.isNotEmpty(tempOrders)&&CollectionUtils.isNotEmpty(selectedCheckBoxItems)){
            createWriteData(dialog);
            List<List<String>> heads = createSheetHead();
            for (int i = 1; i < 11; i++) {
                Integer completionRate=60+i;
                dialog.setCompletionRate(completionRate);
            }
            String fileName = outputFilePath +File.separator+ outputFileName + ".xlsx";
            Thread thread = new Thread(() -> {
                for (int i = 1; i <=30; i++) {
                    try {
                        Integer completionRate=70+i;
                        dialog.setCompletionRate(completionRate);
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            thread.start();
            EasyExcel.write(fileName).head(heads).sheet("明细表").doWrite(writeData);
            thread.stop();
            dialog.setCompletionRate(100);
            dialog.setCompletionRate(101);
        }
    }

    public static boolean outputFileVerify(){
        if(outputFile==null){
            return false;
        }
        if(StringUtils.isBlank(outputFilePath)){
            return false;
        }
        if(StringUtils.isBlank(outputFileName)){
            return false;
        }
        if(CollectionUtils.isEmpty(selectedCheckBoxItems)){
            return false;
        }
        if(CollectionUtils.isEmpty(tempOrders)){
            return false;
        }
        if(ObjectUtil.isEmpty(limitMoney)){
            return false;
        }
        if(StringUtils.isBlank(taxRate)){
            return false;
        }
        if(ObjectUtil.isEmpty(headSelectItem)){
            return false;
        }
        return true;
    }


}
